#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//--------------------------------------------------------------------------
// #define DEBUG
//--------------------------------------------------------------------------
#ifdef DEBUG
    #define OUT printf("# "), //! debug output
#else
    #define OUT if (0)       //! normal output
#endif

//--------------------------------------------------------------------------
// #define ASSERT
//--------------------------------------------------------------------------
#ifndef NDEBUG
    #define ASSERT(cond) if (!(cond))\
                   {\
                       printf("FAIL: %s, in %s, %d, %s\n",\
                       #cond, __FILE__, __LINE__, __PRETTY_FUNCTION__);\
                       exit(3);\
                   }
#else
    #define ASSERT ;
#endif

enum {ERROR, OK};
enum {FALSE, TRUE};
enum {CTOR, PUSH, POP, DTOR, EXIT, HELP, ADD, SUB, MULTIPLY, DIVIDE, SQR};

const int MAXCOMANDSIZE = 1000;
const int NUMBEROFCOMANDS = 11;
const int MAXSTACKSIZE = 1000;
const char * ComandName[NUMBEROFCOMANDS] = {"create", "push", "pop", "destruct", "exit", "help",
                                            "add", "sub", "multiply", "divide", "sqr"};
const char * HelpString =
{
"Select one of these commands:\n\
'create' - create stack;\n\
'push' - add a new item;\n\
'pop' - tack away last element of stack\n\
'destruct' - destruct stack\n\
'exit' - exit the program\n\
'help' - cause help\n\
'add' - add two last elements\n\
'sub' - sub two last elements\n\
'multiply' - multiply two last elements\n\
'divide' - divide two last elements\n\
'sqr' - point to square last element\n"
};


struct stack_t
{
    int *data;
    int size;
    int top;
};

int calculator();
int causeOperation(char * inputComand);
int stack_ctor(stack_t * stk, int stackSize);
int stack_push(stack_t* stk, int element);
int stack_ok(const stack_t* stk);
int* stack_pop(stack_t* stk);
int stack_dtor(stack_t* stk);
int stack_print(const stack_t* stk);
int stack_add(stack_t * stk);
int stack_sub(stack_t * stk);
int stack_multiply(stack_t * stk);
int stack_divide(stack_t * stk);
int stack_sqr(stack_t * stk);

int main()
{
    calculator();

    return 0;
}


int calculator()
{
    stack_t stack = {};
    char * inputComand = (char*) calloc(MAXCOMANDSIZE, sizeof(char));
    if (inputComand == NULL) return ERROR;
    int comand = 0;
    printf("Hello!\n%s", HelpString);

    while (comand != EXIT)
    {
        scanf("%s", inputComand);
        comand = causeOperation(inputComand);
        switch (comand)
        {
            case CTOR:
                if (!stack.data)
                {
                    printf("Input size of your stack:\n");
                    int stackSize = 0;
                    if (scanf("%d", &stackSize) != 1)
                        printf("ERROR: bad input. For example: 5\n");
                    if (1 <= stackSize && stackSize <= MAXSTACKSIZE)
                        stack_ctor(&stack, stackSize);
                    else
                        printf("Incorrect stack size. 1 <= stack size <= 1000\n");
                }
                else
                    printf("Stack is already created\nIt's size is %d\n", stack.size);
                break;
            case PUSH:
            {
                printf("Input your element:\n");
                int element = 0;
                if (scanf("%d", &element) != 1)
                {
                    printf("ERROR: bad input. For example: 4\n");
                }
                else
                {
                    int stackOk = stack_push(&stack, element);
                    if (stackOk == ERROR) printf("ERROR: stack size is over\n");
                }
                break;
            }
            case POP:
            {
                int * pointer  = stack_pop(&stack);
                int element = 0;
                if (pointer != NULL)
                {
                    element = * pointer;
                    * pointer = 666;
                    stack_print(&stack);
                    OUT printf("Value of element: %d\n", element);
                }
                else
                    printf("Stack is empty\n");
                break;
            }
            case DTOR:
                stack_dtor(&stack);
                break;
            case HELP:
                printf("%s", HelpString);
                break;
            case EXIT:
                break;
            case ADD:
                stack_add(&stack);
                break;
            case SUB:
                stack_sub(&stack);
                break;
            case MULTIPLY:
                stack_multiply(&stack);
                break;
            case DIVIDE:
                stack_divide(&stack);
                break;
            case SQR:
                stack_sqr(&stack);
                break;
            default:
                printf("Bad input!\n%s", HelpString);
                break;
        }
    }
    if (stack.data)
    {
        printf("You didn't destroy stack\n");
        stack_dtor(&stack);
    }
    free(inputComand);
    inputComand = NULL;
    return OK;
}

int causeOperation(char * inputComand)
{
    for (int i = 0; i < NUMBEROFCOMANDS; i++)
        if (!strcmp(inputComand, ComandName[i])) return i;
    return 666;
}


int stack_ctor(stack_t * stk, int stackSize)
{
    int * stackArray = (int*) calloc(stackSize, sizeof(int));
    if (stackArray == NULL) return ERROR;
    stk -> data = stackArray;
    stk -> size = stackSize;
    stk -> top = 0;
    for (int i = 0; i < stk -> size; i++)
        stk -> data[i] = 666;
    stack_print(stk);
    return OK;
}

int stack_push(stack_t* stk, int element)
{
    if (stack_ok(stk) && stk->top < stk->size) {
        stk -> data[stk -> top] = element;
        stk -> top++;
    }
    else return ERROR;
    stack_print(stk);
    if (stack_ok(stk)) return OK;
    else               return ERROR;

}

int* stack_pop(stack_t* stk)
{
    int * pointer = NULL;
    if (stack_ok(stk) && stk -> top > 0)
    {
        stk -> top--;
        pointer = &stk -> data[stk -> top];
    }
    return pointer;
}

int stack_dtor(stack_t* stk)
{
    free(stk -> data);
    stk -> data = NULL;
    stk -> size = -1;
    stk -> top = -1;
}

int stack_ok(const stack_t* stk)
{
    if (!stk) return FALSE;
    if (!stk -> data) return FALSE;
    if (stk -> top < 0) return FALSE;
    if (stk -> top > stk -> size) return FALSE;
    return TRUE;
    //! or II
    //return stk && stk -> data && stk -> count >= 0;


//IsBadWritePtr(stk);
return OK;
}

int stack_print(const stack_t* stk)
{
    if (!stack_ok(stk))
    {
        OUT printf("Can't print stack\n");
        return ERROR;
    }
    for (int i = 0; i < stk -> size; i++)
        OUT printf("data[%d] = %d\n", i, stk -> data[i]);
    return OK;
}

int stack_add(stack_t * stk)
{
    int a = 0, b = 0;
    int * pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 1 element\n");
        return ERROR;
    }
    a = *pointer;
    *pointer = 666;

    pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 2 element\n");
        return ERROR;
    }
    b = *pointer;
    *pointer = 666;

    stack_push(stk, a + b);
    return OK;
}
int stack_sub(stack_t * stk)
{
    int a = 0, b = 0;
    int * pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 1 element\n");
        return ERROR;
    }
    a = *pointer;
    *pointer = 666;

    pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 2 element\n");
        return ERROR;
    }
    b = *pointer;
    *pointer = 666;

    stack_push(stk, a - b);
    return OK;
}
int stack_multiply(stack_t * stk)
{
    int a = 0, b = 0;
    int * pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 1 element\n");
        return ERROR;
    }
    a = *pointer;
    *pointer = 666;

    pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 2 element\n");
        return ERROR;
    }
    b = *pointer;
    *pointer = 666;

    stack_push(stk, a * b);
    return OK;
}
int stack_divide(stack_t * stk)
{
    int a = 0, b = 0;
    int * pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 1 element\n");
        return ERROR;
    }
    a = *pointer;
    *pointer = 666;

    pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 2 element\n");
        return ERROR;
    }
    b = *pointer;
    *pointer = 666;
    if (b == 0)
    {
        printf("Dividing on NULL\n");
        stack_push(stk, b);
        stack_push(stk, a);
        return ERROR;
    }
    stack_push(stk, a / b);
    return OK;
}
int stack_sqr(stack_t * stk)
{
    int a = 0;
    int * pointer = stack_pop(stk);
    if (pointer == NULL)
    {
        printf("Can't pop 1 element\n");
        return ERROR;
    }
    a = *pointer;
    *pointer = 666;
    stack_push(stk, a * a);
    return OK;
}

